#!/usr/bin/env python3
###
# @Author: Logan.Li
# @Gitee: https://gitee.com/attacker
# @email: admin@attacker.club
# @Date: 2025-03-14 08:35:30
# @LastEditTime: 2025-03-14 08:35:35
# @Description: 阿里云监控核心模块
###

import os
import json
import logging
from datetime import datetime, timedelta
from typing import Dict, List, Any, Optional
from aliyunsdkcore.client import AcsClient
from aliyunsdkcms.request.v20190101 import (
    DescribeMetricListRequest,
    PutResourceMetricRuleRequest,
    DescribeMetricRuleListRequest
)
from notify import NotificationManager

class AliyunMonitorBase:
    """阿里云监控基类"""
    
    def __init__(self, config: Dict[str, Any]):
        """
        初始化阿里云监控客户端
        
        Args:
            config: 配置字典，包含访问密钥等信息
        """
        self.config = config
        self.client = self._init_client()
        self.notification = NotificationManager(config.get('notification', {}))
        
    def _init_client(self) -> AcsClient:
        """初始化阿里云客户端"""
        return AcsClient(
            self.config['access_key'],
            self.config['secret_key'],
            self.config['region']
        )
        
    def get_metric_data(self, namespace: str, metric_name: str, 
                       dimensions: List[Dict[str, str]], period: int = 60) -> Dict:
        """
        获取指标数据
        
        Args:
            namespace: 指标命名空间
            metric_name: 指标名称
            dimensions: 维度列表
            period: 统计周期（秒）
        """
        try:
            request = DescribeMetricListRequest.DescribeMetricListRequest()
            request.set_Namespace(namespace)
            request.set_MetricName(metric_name)
            request.set_Dimensions(json.dumps(dimensions))
            request.set_Period(str(period))
            
            response = self.client.do_action_with_exception(request)
            return json.loads(response)
        except Exception as e:
            logging.error(f"获取指标数据失败: {e}")
            return None
            
    def create_alarm_rule(self, rule_name: str, namespace: str, metric_name: str,
                         dimensions: List[Dict[str, str]], threshold: float,
                         contact_groups: List[str], period: int = 60,
                         evaluation_count: int = 3) -> bool:
        """
        创建告警规则
        
        Args:
            rule_name: 规则名称
            namespace: 指标命名空间
            metric_name: 指标名称
            dimensions: 维度列表
            threshold: 阈值
            contact_groups: 联系人组列表
            period: 统计周期（秒）
            evaluation_count: 连续超过阈值次数
        """
        try:
            request = PutResourceMetricRuleRequest.PutResourceMetricRuleRequest()
            request.set_RuleName(rule_name)
            request.set_Namespace(namespace)
            request.set_MetricName(metric_name)
            request.set_Dimensions(json.dumps(dimensions))
            request.set_Period(period)
            request.set_Threshold(threshold)
            request.set_ContactGroups(json.dumps(contact_groups))
            request.set_EvaluationCount(evaluation_count)
            
            response = self.client.do_action_with_exception(request)
            return json.loads(response).get('Success', False)
        except Exception as e:
            logging.error(f"创建告警规则失败: {e}")
            return False
            
    def list_alarm_rules(self, namespace: Optional[str] = None) -> List[Dict]:
        """
        获取告警规则列表
        
        Args:
            namespace: 可选，指标命名空间
        """
        try:
            request = DescribeMetricRuleListRequest.DescribeMetricRuleListRequest()
            if namespace:
                request.set_Namespace(namespace)
                
            response = self.client.do_action_with_exception(request)
            return json.loads(response).get('Alarms', {}).get('Alarm', [])
        except Exception as e:
            logging.error(f"获取告警规则列表失败: {e}")
            return []
            
    def format_alarm_message(self, resource_name: str, metric_name: str,
                           current_value: float, threshold: float) -> Dict[str, str]:
        """
        格式化告警消息
        
        Args:
            resource_name: 资源名称
            metric_name: 指标名称
            current_value: 当前值
            threshold: 阈值
        """
        title = f"【告警】{resource_name} {metric_name}超过阈值"
        content = (
            f"### 告警详情\n"
            f"- 资源: {resource_name}\n"
            f"- 指标: {metric_name}\n"
            f"- 当前值: {current_value}\n"
            f"- 阈值: {threshold}\n"
            f"- 时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
        )
        return {"title": title, "content": content}
        
    def send_alarm(self, resource_name: str, metric_name: str,
                  current_value: float, threshold: float) -> bool:
        """
        发送告警通知
        
        Args:
            resource_name: 资源名称
            metric_name: 指标名称
            current_value: 当前值
            threshold: 阈值
        """
        message = self.format_alarm_message(
            resource_name, metric_name, current_value, threshold
        )
        return self.notification.send_notification(
            message["title"], message["content"]
        )

# 使用示例
if __name__ == "__main__":
    # 配置示例
    config = {
        "access_key": "your_access_key",
        "secret_key": "your_secret_key",
        "region": "cn-hangzhou",
        "notification": {
            "dingtalk": {
                "enabled": True,
                "webhook": "your_webhook_url",
                "secret": "your_secret"
            }
        }
    }
    
    monitor = AliyunMonitorBase(config)
    
    # 获取ECS实例CPU使用率
    dimensions = [{"instanceId": "your_instance_id"}]
    metrics = monitor.get_metric_data(
        namespace="acs_ecs_dashboard",
        metric_name="CPUUtilization",
        dimensions=dimensions
    )
    
    # 创建告警规则
    monitor.create_alarm_rule(
        rule_name="CPU使用率告警",
        namespace="acs_ecs_dashboard",
        metric_name="CPUUtilization",
        dimensions=dimensions,
        threshold=80.0,
        contact_groups=["default"]
    )
    
    # 发送测试告警
    monitor.send_alarm(
        resource_name="测试ECS实例",
        metric_name="CPU使用率",
        current_value=90.5,
        threshold=80.0
    )
